
------------------------------------------------------------------------------
-- Group Communication Addon
-- vTRM0.1b by swisshex@gilneas 
-- http://code.google.com/p/wowaddon-trashmobs/
------------------------------------------------------------------------------
-- konglomerat addon
-- v0.3.0a by ashror@gilneas
-- vi:tabstop=4
-- http://addon.daskonglomerat.org/
------------------------------------------------------------------------------



_printf = string.format;
_channelsetup = nil;

------------------------------------------------------------------------------
-- do initial stuff after the addon was loaded
------------------------------------------------------------------------------
function trashmobs_OnLoad()

	-- we need this early
	TRM_LOCALE = GetLocale();

	-- load check
	if(not trashmobs_AddOnCanBeLoaded()) then return; end

	-- zones
	trashmobs_LoadZones();
	
	--make frame closeable with ESC
	tinsert(UISpecialFrames,"TrashmobsFrame");
	
	-- chattype definition
	ChatTypeInfo["TRASHMOBSCHANNEL"] = { sticky = 1 };
	ChatTypeInfo["TRASHMOBSCHANNEL"].r = TRM_D_COLOR_R;
	ChatTypeInfo["TRASHMOBSCHANNEL"].g = TRM_D_COLOR_G;
	ChatTypeInfo["TRASHMOBSCHANNEL"].b = TRM_D_COLOR_B;
	ChatTypeInfo["TRASHMOBSCHANNEL"].id = 413;

	-- initial settings
	trashmobs_settings = {};
	trashmobs_settings["JOIN_MAIN"] = 1;
	trashmobs_settings["JOIN_CLASS"] = 1;
	trashmobs_settings["USE_CHANNEL_PW"] = 0;
	trashmobs_settings["CHANNEL_PW"] = "";
	trashmobs_settings["USE_MEMBER_ALERT"] = 1;
	trashmobs_settings["SETTINGS_VER"] = TRM_SETTINGS_VERSION;
	trashmobs_settings["SHOW_MEMBER_TOOLTIP"] = 1;
	trashmobs_settings["SHOW_MINIMAP_BUTTON"] = 1;
	trashmobs_settings["MINIMAP_BUTTON_POSITION"] = 1;
	trashmobs_settings["COLOR_GROUP_MEMBERS"] = 1;
	trashmobs_settings["LAST_GROUP_DESTINATION"] = "";
	trashmobs_settings["LAST_GROUP_SLOTS_MAX"] = 10;
	trashmobs_settings["LAST_GROUP_COMMENT"] = "";
	trashmobs_settings["ANNOUNCE_NEW_GROUP"] = 1;
	trashmobs_settings["USE_MAINCHAR_INFO"] = 0;
	trashmobs_settings["MAINCHAR_NAME"] = UnitName("player");
	trashmobs_settings["MAINCHAR_CLASS"] = UnitClass("player");
	trashmobs_settings["MAINCHAR_LEVEL"] = UnitLevel("player");
	trashmobs_settings["MAINCHAR_GUILD"] = GetGuildInfo("player");
	trashmobs_settings["MAINCHAR_RACE"] = UnitRace("player");
	trashmobs_settings["COLOR_TWINKS"] = 1;
	trashmobs_settings["REMEMBER_WINDOW_POS"] = 1;
	trashmobs_settings["SHOW_VERSION_INDICATOR"] = 1;
	trashmobs_settings["CHANNEL_SETUP"] = {};
	trashmobs_settings["POST_UPGRADE_DONE"] = nil;
	trashmobs_settings["SHOW_CHANNEL_JOINLEAVE"] = 1;
	trashmobs_settings["ENABLE_FLOOD_PROTECTION"] = 1;
	trashmobs_settings["IGNORE_LIST"]={};

	-- slash command
	SlashCmdList["GROUPCOMMUNICATION"] = trashmobs_HandleSlashCommand;
	SLASH_GROUPCOMMUNICATION1 = "/groupcommunication";
	SLASH_GROUPCOMMUNICATION2 = "/grp";

	-- wrapper
	TRM_FORIG_ChatFrame_OnEvent = ChatFrame_OnEvent;
	ChatFrame_OnEvent = trashmobs_ChatFrame_OnEvent;

	TRM_FORIG_ChatEdit_UpdateHeader = ChatEdit_UpdateHeader;
	ChatEdit_UpdateHeader = trashmobs_ChatEdit_UpdateHeader;

	TRM_FORIG_ChatEdit_OnShow = ChatEdit_OnShow;
	ChatEdit_OnShow = trashmobs_ChatEdit_OnShow;

	TRM_FORIG_SendChatMessage = SendChatMessage;
	SendChatMessage = trashmobs_SendChatMessage;
	
	TRM_FORIG_ChatEdit_SecureTabPressed = ChatEdit_SecureTabPressed;
	ChatEdit_SecureTabPressed = trashmobs_ChatEdit_SecureTabPressed;

	-- event registration
	this:RegisterEvent("ZONE_CHANGED_NEW_AREA");
	this:RegisterEvent("MINIMAP_ZONE_CHANGED");
	this:RegisterEvent("CHAT_MSG_CHANNEL");
	this:RegisterEvent("CHAT_MSG_CHANNEL_LIST");
	this:RegisterEvent("CHAT_MSG_CHANNEL_JOIN");
	this:RegisterEvent("CHAT_MSG_CHANNEL_LEAVE");
	this:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE");
	this:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE_USER");
	this:RegisterEvent("PLAYER_LEVEL_UP");
	this:RegisterEvent("PLAYER_ENTERING_WORLD");
	this:RegisterEvent("PARTY_INVITE_REQUEST");
	this:RegisterEvent("PARTY_MEMBERS_CHANGED");
	this:RegisterEvent("PARTY_LEADER_CHANGED");
	this:RegisterEvent("RAID_ROSTER_UPDATE");
	this:RegisterEvent("VARIABLES_LOADED");

	-- banner
	DEFAULT_CHAT_FRAME:AddMessage(_printf(TRASHMOBS_LOADED,TRM_VERSION),1,1,0);

end;

------------------------------------------------------------------------------
-- initialize
------------------------------------------------------------------------------
function trashmobs_Init()

	local lC, eC = UnitClass("player");
	local prefix = TRM_SUBCHANNEL_PREFIX;
	TRM_CLASS = UnitClass("player");

	local sv = trashmobs_settings["SETTINGS_VER"];
	if(not sv or sv==nil) then sv = 0; end

	-- backward compatibility
	if(sv < 1070) then
		trashmobs_settings["JOIN_MAIN"] = 1;
		trashmobs_settings["JOIN_CLASS"] = 1;
		trashmobs_settings["USE_CHANNEL_PW"] = 0;
	end

	if(sv < 1080) then
		trashmobs_settings["USE_MEMBER_ALERT"] = 1;
	end

	if(sv < 1081) then
		trashmobs_settings["SHOW_MEMBER_TOOLTIP"] = 1;
	end

	if(sv < 1090) then
		trashmobs_settings["SHOW_MINIMAP_BUTTON"] = 0;
		trashmobs_settings["MINIMAP_BUTTON_POSITION"] = 1;
	end
	
	if(sv < 2000) then
		trashmobs_settings["COLOR_GROUP_MEMBERS"] = 1;
		trashmobs_settings["LAST_GROUP_DESTINATION"] = "Zul'Farrak";
		trashmobs_settings["LAST_GROUP_SLOTS_MAX"] = 10;
		trashmobs_settings["LAST_GROUP_COMMENT"] = "";
		trashmobs_settings["ANNOUNCE_NEW_GROUP"] = 1;
	end

	if(sv < 2030) then
		trashmobs_settings["USE_MAINCHAR_INFO"] = 0;
		trashmobs_settings["COLOR_TWINKS"] = 1;
	end

	if(sv<2040) then
		trashmobs_settings["USE_MAINCHAR_INFO"] = 1;
		trashmobs_settings["MAINCHAR_NAME"] = nil;
	end
	
	if(sv<2050) then
		trashmobs_settings["REMEMBER_WINDOW_POS"] = 1;
	end
	
	if(sv<2051) then
		trashmobs_settings["SHOW_VERSION_INDICATOR"] = 1;
	end
	
	if(sv<2060) then
		trashmobs_settings["POST_UPGRADE_DONE"] = nil;
	end

	if(sv<2061) then
		trashmobs_settings["POST_UPGRADE_DONE"] = nil;
		trashmobs_settings["SHOW_CHANNEL_JOINLEAVE"] = 1;
	end
	
	if(sv<2062) then
		trashmobs_settings["ENABLE_FLOOD_PROTECTION"] = 1;
	end
	
	if(sv<2063) then
		trashmobs_settings["CHANNEL_SETUP"] = {};
	end
	
	if(sv<2064) then
		trashmobs_settings["MEMBER_CURRENT_LAYOUT"] = 1;	-- show zone, level, class
	end
	
	if(sv<2070)then
		if(TRM_DATA_CHANNEL~=nil)then
			trashmobs_settings["CHANNEL_NAME"]= TRM_DATA_CHANNEL;	
		else
			trashmobs_settings["CHANNEL_NAME"]= "GroupCommunication";
		end
		
		trashmobs_settings["IGNORE_LIST"]={};
	end

	if(trashmobs_settings["SETTINGS_VER"]~=TRM_SETTINGS_VERSION) then
		trashmobs_settings["SETTINGS_VER"] = TRM_SETTINGS_VERSION;
		TRM_VERSION_UPDATED = true;
	end

	-- set text 
	--TrashmobsFrameCaption:SetText(_printf(TRASHMOBS_MEMBER_DLG_CAPTION));

	trashmobs_UpdateState();

end;

------------------------------------------------------------------------------
-- create default channel setup
------------------------------------------------------------------------------
function trashmobs_InitializeChannelSetup(bForce)

	local class = TRM_CLASS;
	local pname = UnitName("player");
	local col;
	
	if(trashmobs_settings["CHANNEL_NAME"] ~= "" and trashmobs_settings["CHANNEL_NAME"]~=nil) then
		TRM_DATA_CHANNEL=trashmobs_settings["CHANNEL_NAME"];
	else
		trashmobs_settings["CHANNEL_NAME"]=TRM_DATA_CHANNEL;
	end
	
	
	if(not trashmobs_settings["CHANNEL_SETUP"]) then
		trashmobs_settings["CHANNEL_SETUP"] = {};
	end

	if(not trashmobs_settings["CHANNEL_SETUP"][TRM_LOCALE]) then
		trashmobs_settings["CHANNEL_SETUP"][TRM_LOCALE] = {};
	end
	
	_channelsetup = trashmobs_settings["CHANNEL_SETUP"][TRM_LOCALE];

	if(not _channelsetup[pname]) then
		
		_channelsetup[pname] = {};
		_channelsetup[pname][TRM_MAIN_PROMPT] = {
			["enabled"] = true,
			["color"] = {
				["r"] = TRM_D_COLOR_R;
				["g"] = TRM_D_COLOR_G;
				["b"] = TRM_D_COLOR_B;
			};
		};

		for k,v in pairs(TRM_CLASS_CHANNEL) do
			local enabled = false;
			if(trashmobs_TableGetItem(TRM_EN_CLASS_RELATION[k],trashmobs_TableGetItem(TRM_DATA_STRINGS["CLASSES"], TRM_CLASS))>0) then 
				enabled = true; 
			end
			col = RAID_CLASS_COLORS[string.upper(TRM_EN_CLASS_NAMES[k])];
			_channelsetup[pname][k] = {
				["enabled"] = enabled,
				["color"] = {
					["r"] = col.r,
					["g"] = col.g,
					["b"] = col.b,
				},
				["member"] = {}
			};
		end
	
	end

	_channelsetup = _channelsetup[pname];
	for k,v in pairs(_channelsetup) do
		_channelsetup[k]["member"] = {};
	end

end;

------------------------------------------------------------------------------
-- handle slash commands
------------------------------------------------------------------------------
function trashmobs_HandleSlashCommand(param)

	local argv = {};
	local argc = -1;
	local _arg = nil;
	local argall = nil;
	
	-- create argv and argc
	for _arg in string.gmatch(param, "[^%s]+") do
		if(argc<0) then
			cmd = string.upper(_arg);
		else
			table.insert(argv, _arg);
		end
		if(argc>-1) then
			if(argall==nil) then argall = ""; end
			argall = argall .. " ".._arg;
		end
		argc = argc + 1;
	end
	
	-- no command, toggle memberframe
	if(argc<0) then
		trashmobs_ShowMemberFrame();
		return true;
	end

	-- debug
	if(cmd=="DEBUG") then
		TRM_DEBUG = (not TRM_DEBUG);
		if(TRM_DEBUG) then
			trashmobs_sysmsg("debug on");
		else
			trashmobs_sysmsg("debug off");
		end
		return true;
	end

	-- item <ilink>
	if(cmd=="ITEM" and argall~=nil) then
		local itemstr = trashmobs_EncodeItemLink(argall);
		trashmobs_sysmsg(itemstr);
		return true;
	end

	-- rawlink <ilink>
	if(cmd=="RAWLINK" and argall~=nil) then
		local itemstr = string.gsub(argall, "|", "/");
		trashmobs_sysmsg(itemstr);
		return true;
	end

	
	-- version
	if(cmd=="VERSION") then
		trashmobs_sysmsg("---->");
		trashmobs_sysmsg(_printf(TRASHMOBS_LOADED,TRM_VERSION));
		trashmobs_sysmsg(_printf("Revision: %i, by Ashror@Gilneas", TRM_REVISION));
		trashmobs_sysmsg(_printf("http://addon.daskonglomerat.org/"));
		trashmobs_sysmsg("<----");
		return true;
	end
	
	-- version
	if(cmd=="SOUND" and argall~=nil) then
		trashmobs_sysmsg("Sound command: "..argall);
		local msg = _printf("{SND:%d}", argall);
		trashmobs_SendChannelMsg(TRM_DATA_CHANNEL,msg);
		return true;
	end
	
	--testfunction
	if(cmd=="TEST") then
			trashmobs_sysmsg(trashmobs_settings["CHANNEL_NAME"]);
			trashmobs_sysmsg(trashmobs_GetChannelChatFrame(TRM_DATA_CHANNEL));
	end
	
	-- show info
	trashmobs_sysmsg("unknown or wrong command given: "..cmd.." ("..argc.." args)");
	return false;

end;

------------------------------------------------------------------------------
-- channel name to id
------------------------------------------------------------------------------
function trashmobs_ChannelNameToID(cname)
	if(cname==TRM_MAIN_PROMPT) then return 0; end
	for k,v in pairs(TRM_CLASS_CHANNEL) do
		if(k==cname) then return v; end
	end
	return 0;
end;

------------------------------------------------------------------------------
-- id to channel name
------------------------------------------------------------------------------
function trashmobs_IDToChannelName(id)
	if(id==0) then return TRM_MAIN_PROMPT; end
	for k,v in pairs(TRM_CLASS_CHANNEL) do
		if(id == v) then return k; end
	end
	return TRM_MAIN_PROMPT;
end;

------------------------------------------------------------------------------
-- toggle an option
------------------------------------------------------------------------------
function trashmobs_ToggleOption(option,value)

	trashmobs_settings[option] = value;
	trashmobs_UpdateState();

end;

------------------------------------------------------------------------------
-- toggle memberframe visibility
------------------------------------------------------------------------------
function trashmobs_ShowMemberFrame()

	if(TrashmobsFrame:IsVisible()) then
		--TrashmobsFrame:Hide();
		HideUIPanel(TrashmobsFrame);
	else
		--TrashmobsFrame:Show();#
		ShowUIPanel(TrashmobsFrame);
	end
	
end;

------------------------------------------------------------------------------
-- update channels (join / leave if necessary)
------------------------------------------------------------------------------
function trashmobs_JoinDataChannel()

	-- join data channel
	if(trashmobs_GetChannelIndex(TRM_DATA_CHANNEL)==0) then
		JoinChannelByName(TRM_DATA_CHANNEL,TRM_CHANNEL_PW);
		local cf = trashmobs_GetChannelChatFrame(TRM_DATA_CHANNEL);
		trashmobs_AddChannel(cf, TRM_DATA_CHANNEL);
	end

end;



------------------------------------------------------------------------------
-- handle events
------------------------------------------------------------------------------
function trashmobs_OnEvent()

	-- ZONE_CHANGED_NEW_AREA
	if(event=="ZONE_CHANGED_NEW_AREA") then
		if(TRM_FIRST_ZONE_CHANGE) then
			-- channels
			trashmobs_JoinDataChannel();	
			TRM_FIRST_ZONE_CHANGE = false;
		else
			trashmobs_SendMyData(TRM_DATA_MODE_UPDATE);
		end
	end
	
	-- MINIMAP_ZONE_CHANGED
	if(event=="MINIMAP_ZONE_CHANGED") then
		--trashmobs_SendMyData(TRM_DATA_MODE_UPDATE);
	end

	-- VARIABLES_LOADED
	if(event=="VARIABLES_LOADED") then
		trashmobs_Init();
	end

	-- PLAYER_LEVEL_UP
	if (event=="PLAYER_LEVEL_UP") then
		trashmobs_SendMyData(TRM_DATA_MODE_UPDATE);
	end
	
	-- PARTY_LEADER_CHANGED
	if (event=="PARTY_LEADER_CHANGED") then
		if((not trashmobs_IsGroupLeader(true)) and 
			trashmobs_GetNumGroupMembers()>1 and
			TRM_MY_GROUP~=nil) then
				trashmobs_SendMyData(TRM_DATA_MODE_GROUP_REMOVE);
		end
	end

	-- RAID_ROSTER_UPDATE / PARTY_MEMBERS_CHANGED
	if (event=="RAID_ROSTER_UPDATE" or event=="PARTY_MEMBERS_CHANGED") then
		if(TRM_MY_GROUP~=nil and trashmobs_PartyIsUpdated()) then
			trashmobs_SendMyData(TRM_DATA_MODE_UPDATE);
		end
		trashmobs_UpdateGroupFrame();
	end
	
	-- PARTY_INVITE_REQUEST
	if (event=="PARTY_INVITE_REQUEST") then
		local g = TRM_GROUP_DETAILS[arg1];
		if(g~=nil and g["REQUEST_SENT"]==1) then
			AcceptGroup();
			StaticPopup_Hide("PARTY_INVITE");
		end
	end

	-- PLAYER_ENTERING_WORLD
	if(event=="PLAYER_ENTERING_WORLD") then
		if(not trashmobs_settings["MAINCHAR_NAME"] or
			trashmobs_settings["MAINCHAR_NAME"]==nil or
			trashmobs_settings["MAINCHAR_NAME"]==UnitName("player")) then
				trashmobs_SetMainChar();
		end
		trashmobs_InitializeChannelSetup();
		trashmobs_ChannelSetupMenu_OnLoad();
	end

end;

------------------------------------------------------------------------------
-- return chatframe of a channel or default
------------------------------------------------------------------------------
function trashmobs_GetChannelChatFrame(channel)

	for i=1, FCF_GetNumActiveChatFrames() do
		local cf = getglobal("ChatFrame"..i);
		if(cf and cf~=nil) then
			if(trashmobs_ChannelIsOnChatFrame(cf,channel)) then
				return cf;
			end
			--DEFAULT_CHAT_FRAME:AddMessage("not found: "..cf:GetName());
		end
	end

	return DEFAULT_CHAT_FRAME;

end;

------------------------------------------------------------------------------
-- return true if channel is assigned to the given chatframe
------------------------------------------------------------------------------
function trashmobs_ChannelIsOnChatFrame(chatFrame, channel)
	
	for i, v in ipairs(chatFrame.channelList) do
		if(chatFrame.channelList[i]==channel) then
			return true;
		end
	end

	return false;
		
end;

------------------------------------------------------------------------------
-- return the index of a channel or zero if not found
------------------------------------------------------------------------------
function trashmobs_GetChannelIndex(channel)
	
	return GetChannelName(channel);
	-- local list = { GetChannelList(); };

	--for i=1, #list, 2 do
	--	if(channel==list[i+1]) then
	--		return list[i];
	--	end
	--end

	--return 0;
	
end;

------------------------------------------------------------------------------
-- handle chatframe events
------------------------------------------------------------------------------
function trashmobs_ChatFrame_OnEvent(event)

	
	-- data channel
	if (arg9 and arg9~=nil and arg9==TRM_DATA_CHANNEL) then
	
		--Ignore
		if( trashmobs_TableGetItem(trashmobs_settings["IGNORE_LIST"],arg2) ~= -1) then
			return;
		end
		
		-- don't post in the wrong frames
		if(arg9 and arg9~=nil and arg9==TRM_DATA_CHANNEL) then
			if(not trashmobs_ChannelIsOnChatFrame(this,arg9) and
				event=="CHAT_MSG_CHANNEL") then return; end
		end

		if(event=="CHAT_MSG_CHANNEL") then
			trashmobs_ParseDataMsg(arg1, arg2, this);
		end;

		-- system color
		info = ChatTypeInfo["SYSTEM"];

		sr = TRM_D_COLOR_R;
		sg = TRM_D_COLOR_G;
		sb = TRM_D_COLOR_B;

		if(info) then
			sr = info.r;
			sg = info.g;
			sb = info.b;
		end	
		
		-- channel list
		if (event=="CHAT_MSG_CHANNEL_LIST") then
			
			local user = arg1;
			local chan = arg9;

			if(user and user~=nil and chan==TRM_DATA_CHANNEL) then
				--update user list
				for u in string.gmatch(user,"[^,@%s**]+") do
					if(trashmobs_TableGetItem(TRM_ONLINE_USER,u)==-1) then
							trashmobs_TableInsertOnce(TRM_ONLINE_USER,u);
					end
				end

				TRM_SORT_DOWN = (not TRM_SORT_DOWN);
				trashmobs_MemberFrameSort(TRM_LAST_SORT_TYPE);
				trashmobs_UpdateMemberFrame();
			
				if(not TRM_DEBUG) then return; end
			end

		end

		-- join
		if (event=="CHAT_MSG_CHANNEL_JOIN") then

			local user = arg2;
			local chan = arg9;
			local player_link = _printf("|Hplayer:%s|h[%s]|h",user,user);
		
			if (user and user~=nil and chan and chan~=nil) then
				if (chan==TRM_DATA_CHANNEL) then
				
					if(not trashmobs_ChannelIsOnChatFrame(this, chan)) then
						return;
					end

					local mymsg = _printf(TRASHMOBS_JOIN,player_link); 
				
					if(TRM_DEBUG and this == DEFAULT_CHAT_FRAME and TRM_DEBUG_FUN) then
						DoEmote("CHEER", "");
					end

					if(trashmobs_settings["USE_MEMBER_ALERT"]) then
						this:AddMessage(mymsg,sr,sg,sb);
					end

					trashmobs_TableInsertOnce(TRM_ONLINE_USER,user);
					TRM_SORT_DOWN = (not TRM_SORT_DOWN);
					trashmobs_MemberFrameSort(TRM_LAST_SORT_TYPE);
					trashmobs_UpdateMemberFrame();
					return;

				end
			end

		end

		-- user notice
		if (event=="CHAT_MSG_CHANNEL_NOTICE_USER") then

			if(arg1=="SET_MODERATOR" and arg9~=nil and arg9==TRM_DATA_CHANNEL)
				then return; end 
			if(arg1=="UNSET_MODERATOR" and arg9~=nil and arg9==TRM_DATA_CHANNEL)
				then return; end
			if(arg1=="OWNER_CHANGED" and arg9~=nil and arg9==TRM_DATA_CHANNEL)
				then return; end
			if(arg1=="PLAYER_ALREADY_MEMBER" and arg9~=nil and arg9==TRM_DATA_CHANNEL)
				then return; end
			
			trashmobs_debugp("TRM FIXME CHANNELNOTICE: "..arg1);

		end

		-- self join and leave
		if (event=="CHAT_MSG_CHANNEL_NOTICE") then

			local cmd = arg1;
			local chan = arg9;

			if(cmd=="YOU_JOINED" and chan==TRM_DATA_CHANNEL) then
				
				if(not trashmobs_ChannelIsOnChatFrame(this, chan)) then
					return;
				end
				
				trashmobs_SendMyData(TRM_DATA_MODE_FIRST);

				local mymsg = TRASHMOBS_YOUJOINED;
				this:AddMessage(mymsg,sr,sg,sb);
				trashmobs_UpdateWhoList();
				return;
			end

			if(cmd=="YOU_LEFT" and chan==TRM_DATA_CHANNEL) then
				
				if(not trashmobs_ChannelIsOnChatFrame(this, chan)) then
					return;
				end
				
				TRM_GROUP_LIST = {};

				local mymsg = TRASHMOBS_YOULEFT;
				this:AddMessage(mymsg,sr,sg,sb);
				trashmobs_UpdateWhoList(true);
				return;
			end

		end

		-- leave
		if (event=="CHAT_MSG_CHANNEL_LEAVE") then
			
			local user = arg2;
			local chan = arg9;

			if (user and user~=nil and chan and chan~=nil) then
				if (chan==TRM_DATA_CHANNEL) then
				
					if(not trashmobs_ChannelIsOnChatFrame(this, chan)) then
						return;
					end

					local mymsg = _printf(TRASHMOBS_LEAVE,user);

					if(TRM_DEBUG and this == DEFAULT_CHAT_FRAME and TRM_DEBUG_FUN) then
						DoEmote("CRY", "");
					end
		
					if(trashmobs_settings["USE_MEMBER_ALERT"]) then
						this:AddMessage(mymsg,sr,sg,sb);
					end

					-- remove from online list
					trashmobs_TableRemoveItem(TRM_ONLINE_USER, user);
					trashmobs_RemoveGroup(user);

					if(TRM_ONLINE_USER_DETAILS[user] ~= nil) then
						TRM_ONLINE_USER_DETAILS[user]["RESPONSE_SENT"] = nil;
					end

					TRM_SORT_DOWN = (not TRM_SORT_DOWN);
					trashmobs_MemberFrameSort(TRM_LAST_SORT_TYPE);
					trashmobs_UpdateMemberFrame();
					trashmobs_UpdateGroupFrame();

					return;
				end
			end
			
		end
	
		if(not TRM_DEBUG) then return; end
	
	end -- TRM_DATA_CHANNEL

	TRM_FORIG_ChatFrame_OnEvent(event);

end;

------------------------------------------------------------------------------
-- assign channel to chatframe
------------------------------------------------------------------------------
function trashmobs_AddChannel(chatFrame, channel)

	local frameID = chatFrame:GetID();

	if (AddChatWindowChannel(frameID, channel)) then
		local i = 1;

		while (chatFrame.channelList[i]) do
			i = i + 1;
		end

		chatFrame.channelList[i] = channel;
		chatFrame.zoneChannelList[i] = 0;
	end

end;

------------------------------------------------------------------------------
-- update chatedit prompt (header)
------------------------------------------------------------------------------
function trashmobs_ChatEdit_UpdateHeader(editBox)

	local rtarget = editBox:GetAttribute("channelTarget");
	local ct = editBox:GetAttribute("chatType");

	if(rtarget and
		rtarget==trashmobs_GetChannelIndex(TRM_DATA_CHANNEL) and
		ct=="CHANNEL") then
		editBox:SetAttribute("chatType", "TRASHMOBSCHANNEL");
	end

	local old_chat_channel_send = getglobal("CHAT_CHANNEL_SEND");
	local target = editBox:GetAttribute("trmTarget");
	local ct = editBox:GetAttribute("chatType");

	TRM_WAS_LAST_SEND = false;
	if(not target or target==nil) then target = TRM_MAIN_PROMPT; end


	ChatTypeInfo["TRASHMOBSCHANNEL"].r =
		_channelsetup[target]["color"]["r"];
	ChatTypeInfo["TRASHMOBSCHANNEL"].g =
		_channelsetup[target]["color"]["g"];
	ChatTypeInfo["TRASHMOBSCHANNEL"].b =
		_channelsetup[target]["color"]["b"];

	if(ct=="TRASHMOBSCHANNEL") then

		local pref = "";
		if(target~=TRM_MAIN_PROMPT) then pref=TRM_SUBCHANNEL_PREFIX; end
		setglobal("CHAT_TRASHMOBSCHANNEL_SEND", pref..target..":");
		TRM_LAST_SEND_CHANNEL = target;
		TRM_WAS_LAST_SEND = true;

	end
	
	TRM_FORIG_ChatEdit_UpdateHeader(editBox);

end;

------------------------------------------------------------------------------
-- handle chatedit tab
------------------------------------------------------------------------------
function trashmobs_ChatEdit_SecureTabPressed()
	if (this:GetAttribute("chatType")=="TRASHMOBSCHANNEL") then
		local target = this:GetAttribute("trmTarget");
		this:SetAttribute("trmTarget", trashmobs_GetNextChannelTarget(target));
		ChatEdit_UpdateHeader(this);
	end	
	TRM_FORIG_ChatEdit_SecureTabPressed();
end;

------------------------------------------------------------------------------
-- get next channel target
------------------------------------------------------------------------------
function trashmobs_GetNextChannelTarget(oldTarget)
	local newTarget = nil;
	local found = false;
	local first = nil;
	table.foreach(_channelsetup,function(k,v)
		if(newTarget~=nil or not v["enabled"]) then return; end
		if(first==nil) then first = k; end
		if(found) then
			newTarget = k;
			return;
		end
		if(k==oldTarget) then found = true; end
	end);
	if(newTarget==nil and first~=nil) then newTarget = first; end
	if(newTarget==nil) then newTarget = TRM_MAIN_PROMPT; end
	return newTarget;
end;

------------------------------------------------------------------------------
-- handle chatedit show-event
------------------------------------------------------------------------------
function trashmobs_ChatEdit_OnShow()

	if (TRM_WAS_LAST_SEND==true) then
		this:SetAttribute("chatType", "TRASHMOBSCHANNEL");
		this:SetAttribute("trmTarget", TRM_LAST_SEND_CHANNEL);
	end

	TRM_FORIG_ChatEdit_OnShow();

end;

------------------------------------------------------------------------------
-- open kgm chat
------------------------------------------------------------------------------
function trashmobs_OpenChat()
	if(not _channelsetup[TRM_MAIN_PROMPT]["enabled"]) then
		trashmobs_sysmsg(_printf(TRASHMOBS_ROOM_INACTIVE,TRM_MAIN_PROMPT));
		return;
	end
	trashmobs_DoOpenChat(TRM_MAIN_PROMPT);
end

------------------------------------------------------------------------------
-- open class chat
------------------------------------------------------------------------------
function trashmobs_OpenClassChat()

	if(not _channelsetup[trashmobs_GetManClass(TRM_CLASS)]["enabled"]) then
		trashmobs_sysmsg(_printf(TRASHMOBS_ROOM_INACTIVE,TRM_CLASS));
		return;
	end
	trashmobs_DoOpenChat(trashmobs_GetManClass(TRM_CLASS));
end

------------------------------------------------------------------------------
-- open a given chat
------------------------------------------------------------------------------
function trashmobs_DoOpenChat(channel)
	
	local chatFrame = DEFAULT_CHAT_FRAME;
	local sType = "TRASHMOBSCHANNEL";
	
	chatFrame.editBox:SetAttribute("trmTarget", channel);
	chatFrame.editBox:SetAttribute("chatType", sType);
	trashmobs_ChatEdit_UpdateHeader(chatFrame.editBox);
	ChatFrame_OpenChat("");

end;

------------------------------------------------------------------------------
-- encode item links for transportation
------------------------------------------------------------------------------
function trashmobs_EncodeItemLink(str)
	
	local regex = "|c(%x+)|H([^|]-)|h%[([^%]]-)%]|h|r";
	local code = "{ITEMLINK'%1'%2'%3}";

	if (str) then
		str = string.gsub(str,regex,code);
	end

	return str;

end

------------------------------------------------------------------------------
-- decode item link
------------------------------------------------------------------------------
function trashmobs_DecodeItemLink(str)

	local regex = "{ITEMLINK'(%x+)'([^']-)'([^}]-)}";
	local itemlink = "|c%1|H%2|h[%3]|h|r";
	
	-- backward compatibility
	local regex_old = "{ITEMLINK:(%x+):([%d-]-):([%d-]-):([%d-]-):"..
		"([%d-]-):([%d-]-):([%d-]-):([%d-]-:[%d-]-):([^}]-)}";
	local itemlink_old = "|c%1|Hitem:%2:%3:%4:%5:%6:%7:%8|h[%9]|h|r";

	if (str) then
		str = string.gsub(str,regex,itemlink);
		str = string.gsub(str,regex_old,itemlink_old);
	end

	return str;

end

------------------------------------------------------------------------------
-- handle "SendChatMessage" event
------------------------------------------------------------------------------
function trashmobs_SendChatMessage(msg,...)
	
	local arg = {...};
	local system, language, channel = unpack(arg);

	if (string.sub(system,1,18)=="TRASHMOBSCHANNEL") then
		local dmsg = _printf("{CH:%d}%s",
			trashmobs_ChannelNameToID(TRM_LAST_SEND_CHANNEL),
			trashmobs_EncodeItemLink(msg));

		trashmobs_SendChannelMsg(TRM_DATA_CHANNEL,dmsg);
		return;
	end

	TRM_FORIG_SendChatMessage(msg, unpack(arg));

end

------------------------------------------------------------------------------
-- get item from table
------------------------------------------------------------------------------
function trashmobs_TableInsertOnce(t, item)
	for i,v in ipairs(t) do
		if(v==item) then return false; end
	end
	table.insert(t, item);
	return true;
end;

------------------------------------------------------------------------------
-- get item from table
------------------------------------------------------------------------------
function trashmobs_TableGetItem(t,item)

	for i,v in ipairs(t) do
		if(v==item) then return i; end
	end

	return -1;
		
end;

------------------------------------------------------------------------------
-- remove item from table
------------------------------------------------------------------------------
function trashmobs_TableRemoveItem(t,item)

	for i,v in ipairs(t) do
		if(t[i] == item) then
			table.remove(t,i);
			return true;
		end
	end
	
	return false;

end;

------------------------------------------------------------------------------
-- clear userlist and request "who" update
------------------------------------------------------------------------------
function trashmobs_UpdateWhoList()
	
	-- clear table
	while(#TRM_ONLINE_USER~=nil and
		#TRM_ONLINE_USER>0) do
			table.remove(TRM_ONLINE_USER);
	end
	
	ListChannelByName(TRM_DATA_CHANNEL);

end;

------------------------------------------------------------------------------
-- handle click on a member
------------------------------------------------------------------------------
function trashmobs_SetSelectedMember(id)
		
	local scrollbar = TrashmobsMemberFrameScrollFrame;
	local offset = FauxScrollFrame_GetOffset(scrollbar);

	TRM_SELECTED_MEMBER = offset + id;
	trashmobs_UpdateMemberFrame();
	
end;

------------------------------------------------------------------------------
-- invite the selected member
------------------------------------------------------------------------------
function trashmobs_InviteSelectedMember()

	InviteUnit(TRM_ONLINE_USER[TRM_SELECTED_MEMBER]);
		
end;

------------------------------------------------------------------------------
-- toggle member frame layout
------------------------------------------------------------------------------
function trashmobs_ToggleMemberFrameLayout()
	if(trashmobs_settings["MEMBER_CURRENT_LAYOUT"]==1) then
		trashmobs_settings["MEMBER_CURRENT_LAYOUT"]=2;
		
	else
		trashmobs_settings["MEMBER_CURRENT_LAYOUT"]=1;
		
	end
	trashmobs_UpdateMemberFrame();

end;

------------------------------------------------------------------------------
-- update member frame
------------------------------------------------------------------------------
function trashmobs_UpdateMemberFrame()

	local list = TRM_ONLINE_USER;
	local scrollbar = TrashmobsMemberFrameScrollFrame;
	local offset = FauxScrollFrame_GetOffset(scrollbar);
	local pf = "TrashmobsMemberFrameButton";
	local count = #list;

	if(count==nil) then return; end;
	if(count < TRM_MEMBER_LIST_MAX) then
		scrollbar:Hide();
		WhoFrameColumn_SetWidth(92, TrashmobsMemberFrameColumnHeaderClass);
		WhoFrameColumn_SetWidth(81, TrashmobsMemberFrameColumnHeaderRole);
	else
		scrollbar:Show();	
		WhoFrameColumn_SetWidth(71, TrashmobsMemberFrameColumnHeaderClass);
		WhoFrameColumn_SetWidth(61, TrashmobsMemberFrameColumnHeaderRole);
	end

	if((TRM_SELECTED_MEMBER>count or TRM_SELECTED_MEMBER<1)
		and count>0) then
			TRM_SELECTED_MEMBER = 1;
	end

	for i=1, TRM_MEMBER_LIST_MAX, 1 do
		local index = offset + i;
		local name = list[index];
		local details = TRM_ONLINE_USER_DETAILS;
		local button = getglobal(pf..i);
		local class = "";
		local level = "";
		local location = TRASHMOBS_OLD_VERSION;
		local talents="";
		local maintalent="";
		local role="";
		local area = "";
		local version = "";
		local v_comp = 0;
		local pref = "";

		if(not list[index] or list[index]==nil) then
			name = "";
		end

		if(details[name]~=nil) then

			v_comp = trashmobs_CmpVersionStr(details[name]["VERSION"]);
			if(trashmobs_settings["SHOW_VERSION_INDICATOR"]) then
				if(v_comp<0) then pref = "|cff703030<|h|r "; end
				if(v_comp>0) then pref = "|cff907010>|h|r "; end
				if(v_comp==0) then pref = "|cff509050=|h|r "; end
			end

			if(details[name]["CLASS"]~=nil) then
				class = details[name]["CLASS"];
			end
			if(details[name]["VERSION"]~=nil) then
				version = details[name]["VERSION"];
			end
			if(details[name]["LEVEL"]~=nil) then
				level = details[name]["LEVEL"];
			end
			if(details[name]["LOCATION"]~=nil) then
				location = details[name]["LOCATION"];
			end
			if(details[name]["AREA"]~=nil) then
				area = details[name]["AREA"];
			end
			if(details[name]["TALENTS"]~=nil) then
				talents = details[name]["TALENTS"];
			end
			if(details[name]["MAINTALENT"]~=nil) then
				maintalent = details[name]["MAINTALENT"];
			end
			if(details[name]["ROLE"]~=nil) then
				role = details[name]["ROLE"];
			end

			getglobal(pf..i.."Zone"):SetWidth(120);
			getglobal(pf..i.."Zone"):SetTextColor(1,1,1);
		else
			if(trashmobs_settings["SHOW_VERSION_INDICATOR"]) then
				pref = "|cff505050?|h|r ";
			end
			getglobal(pf..i.."Zone"):SetWidth(160);
			getglobal(pf..i.."Zone"):SetTextColor(0.6,0.3,0.3);
		end

		getglobal(pf..i.."Name"):SetTextColor(NORMAL_FONT_COLOR.r,
					NORMAL_FONT_COLOR.g,NORMAL_FONT_COLOR.b);
		if(trashmobs_settings["COLOR_GROUP_MEMBERS"]) then
			if(trashmobs_IsPartyMember(name)) then
				getglobal(pf..i.."Name"):SetTextColor(0.5,0.5,0.5);
			end
			if(trashmobs_TableGetItem(TRM_FLOODP_IGNORE_LIST, name)~=-1) then
				getglobal(pf..i.."Name"):SetTextColor(0.8,0.2,0.2);
			end
		end

		if(trashmobs_settings["COLOR_TWINKS"]) then
			if(details[name]~=nil and details[name]["IS_TWINK"]) then
				getglobal(pf..i.."Name"):SetTextColor(0.5,0.8,0.1);
			end
		end

		getglobal(pf..i.."Name"):SetText(pref..name);
		getglobal(pf..i.."Class"):SetText(class);
		-- Text Color for Classname
		local ENclass=trashmobs_GetENClass(class);
		if(ENclass~=nil) then
			local color=RAID_CLASS_COLORS[string.upper(ENclass)];
			getglobal(pf..i.."Class"):SetTextColor(color["r"],color["g"],color["b"]);
		end
		
		
		getglobal(pf..i.."Level"):SetText(level);
		getglobal(pf..i.."Zone"):SetText(location);
		getglobal(pf..i.."Talents"):SetText(talents);
		getglobal(pf..i.."MainTalent"):SetText(maintalent);
		getglobal(pf..i.."Role"):SetText(role);

		-- highlight
		if(TRM_SELECTED_MEMBER==index) then
			button:LockHighlight();
		else
			button:UnlockHighlight();
		end

		if(count < TRM_MEMBER_LIST_MAX) then
			getglobal(pf..i):SetWidth(333);
			getglobal(pf..i.."Class"):SetWidth(92);
			getglobal(pf..i.."Role"):SetWidth(81);
		else
			getglobal(pf..i):SetWidth(298);
			getglobal(pf..i.."Class"):SetWidth(71);
			getglobal(pf..i.."Role"):SetWidth(61);
		end
		
		--show and hide Layout
		if(trashmobs_settings["MEMBER_CURRENT_LAYOUT"] ==1) then
			-- Headers
			HideUIPanel(TrashmobsMemberFrameColumnHeaderTalents);
			HideUIPanel(TrashmobsMemberFrameColumnHeaderMainTalent);
			HideUIPanel(TrashmobsMemberFrameColumnHeaderRole);
			
			ShowUIPanel(TrashmobsMemberFrameColumnHeaderZone);
			ShowUIPanel(TrashmobsMemberFrameColumnHeaderLevel);
			ShowUIPanel(TrashmobsMemberFrameColumnHeaderClass);
			--Details
			getglobal(pf..i.."Class"):Show();
			getglobal(pf..i.."Level"):Show();
			getglobal(pf..i.."Zone"):Show();
			
			getglobal(pf..i.."Talents"):Hide();
			getglobal(pf..i.."MainTalent"):Hide();
			getglobal(pf..i.."Role"):Hide();
		else
			--Headers
			HideUIPanel(TrashmobsMemberFrameColumnHeaderZone);
			HideUIPanel(TrashmobsMemberFrameColumnHeaderLevel);
			HideUIPanel(TrashmobsMemberFrameColumnHeaderClass);
			
			ShowUIPanel(TrashmobsMemberFrameColumnHeaderTalents);
			ShowUIPanel(TrashmobsMemberFrameColumnHeaderMainTalent);
			ShowUIPanel(TrashmobsMemberFrameColumnHeaderRole);
			--Details
			getglobal(pf..i.."Class"):Hide();
			getglobal(pf..i.."Level"):Hide();
			getglobal(pf..i.."Zone"):Hide();
			
			getglobal(pf..i.."Talents"):Show();
			getglobal(pf..i.."MainTalent"):Show();
			getglobal(pf..i.."Role"):Show();
		end
		
		
		-- visibility
		if(index>count) then
			button:Hide();
		else
			button:Show();
		end
	end

	-- mainchar info
	if(not trashmobs_settings["USE_MAINCHAR_INFO"] or
		trashmobs_settings["MAINCHAR_NAME"]==nil or
		trashmobs_settings["MAINCHAR_NAME"]=="") then
		TrashmobsMemberFrameMainCharInfo:SetText(
			TRASHMOBS_MAINCHAR_INFO_NO_MAINCHAR);
	else
		TrashmobsMemberFrameMainCharInfo:SetText(
			_printf(TRASHMOBS_MAINCHAR_INFO,
			trashmobs_settings["MAINCHAR_NAME"]));
	end
	
	--newest version
	TrashmobsMemberFrameNewestVersion:SetText(_printf(TRASHMOBS_NEWEST_VERSION,TRM_NEWEST_VERSION));

	-- update count info
	TrashmobsMemberFrameMemberCount:SetText(
		_printf(TRASHMOBS_MEMBER_COUNT,count));

	-- update scrollbar
	FauxScrollFrame_Update(scrollbar, count, TRM_MEMBER_LIST_MAX, 
		TRM_MEMBER_BUTTON_HEIGHT);

	-- action button states
	if(count<1) then
		TrashmobsMemberFrameInviteButton:Disable();
		TrashmobsMemberFrameSendPMButton:Disable();
	else
		TrashmobsMemberFrameInviteButton:Enable();
		TrashmobsMemberFrameSendPMButton:Enable();
	end

end;

------------------------------------------------------------------------------
-- handle click on a group
------------------------------------------------------------------------------
function trashmobs_SetSelectedGroup(id, collapse)
		
	if(collapse==nil) then collapse = true; end
	local scrollbar = TrashmobsGroupFrameScrollFrame;
	local offset = FauxScrollFrame_GetOffset(scrollbar);
	local button = getglobal("TrashmobsGroupFrameButton"..id);
	if(not button or button==nil) then return; end
	if(not button.isHeader) then
		if(id>1) then
			trashmobs_SetSelectedGroup(id - 1, false);
		end
		return;
	end
	local item = button.item;
	local details = TRM_GROUP_DETAILS;
	if(not details[item] or details[item]==nil) then return; end
	local isCollapsed = (not details[item]["IS_COLLAPSED"]);

	TRM_SELECTED_GROUP = offset + button.headerIndex;
	if(collapse==true) then details[item]["IS_COLLAPSED"] = isCollapsed; end
	trashmobs_UpdateGroupFrame();
	
end;

------------------------------------------------------------------------------
-- flood protection
-- returns true on flood or false
------------------------------------------------------------------------------
function trashmobs_FloodProtection(member)

	if(trashmobs_settings["ENABLE_FLOOD_PROTECTION"]~=1) then
		TRM_FLOODP_IGNORE_LIST = {};
		return false;
	end

	local time = GetTime();
	local details = TRM_ONLINE_USER_DETAILS[member];

	if(not details or details==nil) then return false; end
	
	-- prepare
	if(not details["LAST_MSG_TIME"] or
		details["LAST_MSG_TIME"]==nil) then
			details["LAST_MSG_TIME"] = time;
	end
	
	if(not details["FP_HIT_COUNT"] or
		details["FP_HIT_COUNT"]==nil) then
			details["FP_HIT_COUNT"] = 0;
	end
	
	-- remove if time expired, return true if still ignored
	if(trashmobs_TableGetItem(TRM_FLOODP_IGNORE_LIST, member)>-1) then
		if(time >= (details["LAST_MSG_TIME"]+TRM_FLOODP_IGNORE_TIME)) then
			trashmobs_TableRemoveItem(TRM_FLOODP_IGNORE_LIST, member);
			trashmobs_sysmsg(_printf(TRASHMOBS_FLOODP_UNIGNORED,member));
			trashmobs_UpdateMemberFrame();
		else
			trashmobs_debugp("ignored: "..member);
			return true; -- ignored
		end
	end
	
	-- check
	if(details["LAST_MSG_TIME"] >= (time-TRM_FLOODP_TIME)) then
		details["FP_HIT_COUNT"] = details["FP_HIT_COUNT"] + 1;
	else
		details["FP_HIT_COUNT"] = 0;
		details["LAST_MSG_TIME"] = time;
	end
	
	-- ignore if flood protection hits
	if(details["FP_HIT_COUNT"] >= TRM_FLOODP_MAX_MESSAGES) then
		trashmobs_TableInsertOnce(TRM_FLOODP_IGNORE_LIST, member);
		details["FP_HIT_COUNT"] = 0;
		details["LAST_MSG_TIME"] = time;
		trashmobs_sysmsg(_printf(TRASHMOBS_FLOODP_IGNORED,
			member,TRM_FLOODP_IGNORE_TIME));
		trashmobs_UpdateMemberFrame();
		return true;
	end

	return false;

end;

------------------------------------------------------------------------------
-- update group frame
------------------------------------------------------------------------------
function trashmobs_UpdateGroupFrame()

	local list = TRM_GROUP_LIST;
	local details = TRM_GROUP_DETAILS;
	local scrollbar = TrashmobsGroupFrameScrollFrame;
	local offset = FauxScrollFrame_GetOffset(scrollbar);
	local pf = "TrashmobsGroupFrameButton";
	local itemList = {};
	local headerIndex = 1;
	local lastHeaderItem = "";

	if(list==nil) then return; end;

	-- prepare data
	for i,v in ipairs(list) do
		table.insert(itemList,"H"..v);
		local group_t = details[v];
		if(group_t and group_t~=nil and group_t["IS_COLLAPSED"]) then
			local member_t = details[v]["MEMBER"];
			--table.insert(itemList,"M"..v);
			if(member_t and member_t~=nil) then
				local mcnt = #member_t;
				local no_data_c =
					(group_t["SLOTS_MAX"] - group_t["SLOTS_FREE"]) - mcnt;
				for ii,vm in ipairs(member_t) do
					table.insert(itemList,"M"..vm);
				end
				for iii=1, no_data_c, 1 do
					table.insert(itemList,"M"..TRASHMOBS_UNKNOWN);
				end
			end
		end
	end

	local count = #itemList;
	if(count < TRM_GROUP_LIST_MAX) then
		scrollbar:Hide();
	else
		scrollbar:Show();	
	end

	if((TRM_SELECTED_GROUP>#list or TRM_SELECTED_GROUP<1)
		and #list>0) then
			TRM_SELECTED_GROUP = 1;
	end

	if(TRM_MY_GROUP~=nil) then
		TrashmobsGroupFrameToggleGroupButton:SetText(TRASHMOBS_DEL_GROUP);
	else
		TrashmobsGroupFrameToggleGroupButton:SetText(TRASHMOBS_ADD_GROUP);
	end

	TrashmobsGroupFrameToggleGroupButton:Enable();
	if(trashmobs_IsInGroup() and not trashmobs_IsGroupLeader()) then
		TrashmobsGroupFrameToggleGroupButton:Disable();
	end
	
	TrashmobsGroupFrameHighlight:Hide();

	for i=1, TRM_GROUP_LIST_MAX, 1 do
		local index = offset + i;
		local button = getglobal(pf..i);
		local buttonTag = getglobal(pf..i.."Tag");
		local buttonText = getglobal(pf..i.."Txt");
		local buttonHighlight = getglobal(pf..i.."Highlight");
		
		button.isHeader = false;

		if(itemList[index]==nil) then
			button:Hide();
		else
		
			local itemType = string.sub(itemList[index],1,1);
			local item = string.sub(itemList[index],2);
			
			button.item = item;
			
			if(itemType=="H") then
				local slotsUsed =
					details[item]["SLOTS_MAX"]-details[item]["SLOTS_FREE"];
				if(slotsUsed<1) then slotsUsed = 1; end
				local title_text = _printf("%s (%s)",
					details[item]["DESTINATION"],item);
				local tag_text = _printf("%i/%i",
					slotsUsed, details[item]["SLOTS_MAX"]);

				button.isHeader = true;
				button.headerIndex = headerIndex;
				buttonText:SetText(title_text);
				buttonText:SetTextColor(NORMAL_FONT_COLOR.r,
					NORMAL_FONT_COLOR.g,NORMAL_FONT_COLOR.b);
				buttonTag:SetText(tag_text);
				buttonTag:SetTextColor(NORMAL_FONT_COLOR.r,
					NORMAL_FONT_COLOR.g,NORMAL_FONT_COLOR.b);
								
				if (details[item]["IS_COLLAPSED"]) then
					button:SetNormalTexture(
						"Interface\\Buttons\\UI-MinusButton-Up"); 
				else
					button:SetNormalTexture(
						"Interface\\Buttons\\UI-PlusButton-Up");
				end

				buttonHighlight:SetTexture(
					"Interface\\Buttons\\UI-PlusButton-Hilight");

				-- highlight
				button:UnlockHighlight();
				if(TRM_SELECTED_GROUP==headerIndex) then
					TrashmobsGroupFrameHighlight:SetPoint(
						"TOPLEFT", pf..i, "TOPLEFT", 0, 0);
					TrashmobsGroupFrameHighlight:Show();
					button:LockHighlight();
				end
					
				lastHeaderItem = item;
				headerIndex = headerIndex + 1;

			elseif(itemType=="M") then
			
				if(i==1) then
					for a=1, index do
						local myi = index - a;
						if(itemList[myi]~=nil and string.sub(itemList[myi],1,1)=="H") then
							lastHeaderItem = string.sub(itemList[myi],2);
							a = index;
						end
					end
				end
			
				if(details[lastHeaderItem]~=nil and
					details[lastHeaderItem]["MEMBER_CLASSES"]~=nil and
					details[lastHeaderItem]["MEMBER_CLASSES"][item]~=nil) then
					class = details[lastHeaderItem]["MEMBER_CLASSES"][item];
				else
					if(lastHeaderItem==item and
						TRM_ONLINE_USER_DETAILS[item]~=nil) then
					 	class = TRM_ONLINE_USER_DETAILS[item]["CLASS"];
					else
						class = "??";
					end
				end
				
				buttonText:SetText("  "..item);
				buttonText:SetTextColor(0.5,0.5,0.5);
				buttonTag:SetText(class);
				
				--Class Coloring
				if(class ~= "??") then
					local ENclass=trashmobs_GetENClass(class);
					if(ENclass ~= nil and ENclass ~= "") then
						local colorClass=RAID_CLASS_COLORS[string.upper(ENclass)];
						buttonTag:SetTextColor(colorClass["r"],colorClass["g"],colorClass["b"]);
					end
				end
				
				button:SetNormalTexture("");
				buttonHighlight:SetTexture("");
				
			end

			-- adjust width
			if(count>TRM_GROUP_LIST_MAX) then
				button:SetWidth(298);
			else
				button:SetWidth(313);
			end
			
			button:Show();

		end

	end

	-- update count info
	TrashmobsGroupFrameGroupCount:SetText(
		_printf(TRASHMOBS_GROUP_COUNT,#list));

	-- update scrollbar
	FauxScrollFrame_Update(scrollbar, count, TRM_GROUP_LIST_MAX, 
		TRM_GROUP_BUTTON_HEIGHT);

	-- action button states
	if(count<1) then
		TrashmobsGroupFrameEnterGroupButton:Disable();
		TrashmobsGroupFrameEnterGroupButton:SetText(TRASHMOBS_ENTER_GROUP);
		return;
	end

	local selgroup = TRM_GROUP_LIST[TRM_SELECTED_GROUP];
	TRM_GROUP_DETAILS[selgroup]["REQUEST_STATE"] = TRASHMOBS_ENTER_GROUP;
	TrashmobsGroupFrameEnterGroupButton:Enable();

	if(TRM_GROUP_DETAILS[selgroup]["SLOTS_FREE"]<1) then
		TrashmobsGroupFrameEnterGroupButton:Disable();		
	end

	if(TRM_GROUP_DETAILS[selgroup]["REQUEST_SENT"]==1) then
		TRM_GROUP_DETAILS[selgroup]["REQUEST_STATE"] =
			TRASHMOBS_REQUEST_SENT;
		TrashmobsGroupFrameEnterGroupButton:Disable();
	end
	
	if(trashmobs_IsInGroup()) then
		TRM_GROUP_DETAILS[selgroup]["REQUEST_STATE"] = TRASHMOBS_ENTERED;
		TrashmobsGroupFrameEnterGroupButton:Disable();
	end
			
	if(selgroup==UnitName("player")) then
		TRM_GROUP_DETAILS[selgroup]["REQUEST_STATE"] = TRASHMOBS_OWN_GROUP;
		TrashmobsGroupFrameEnterGroupButton:Disable();
	end

	TrashmobsGroupFrameEnterGroupButton:SetText(
		TRM_GROUP_DETAILS[selgroup]["REQUEST_STATE"]);

end;

-----------------------------------------------------------------------------
-- handle channel color changed
-----------------------------------------------------------------------------
function trashmobs_SetChatTypeColor()
	local room = UIDROPDOWNMENU_MENU_VALUE;
	local uchannel = string.upper(room);
	local r,g,b = ColorPickerFrame:GetColorRGB();
	_channelsetup[room]["color"]["r"] = r;
	_channelsetup[room]["color"]["g"] = g;
	_channelsetup[room]["color"]["b"] = b;
end;

-----------------------------------------------------------------------------
-- prepare chatedit to send a message to the selected member
-----------------------------------------------------------------------------
function trashmobs_SendMessageToSelectedMember()

	local member = TRM_ONLINE_USER[TRM_SELECTED_MEMBER];
	if(not member or member==nil) then return; end

	if ( not ChatFrameEditBox:IsVisible() ) then
		ChatFrame_OpenChat("/w "..member.." ");
	else
		ChatFrameEditBox:SetText("/w "..member.." ");
	end

	ChatEdit_ParseText(ChatFrame1.editBox, 0);

end;

-----------------------------------------------------------------------------
-- handle "onenter" event of the memberbuttons (create and show tooltip)
-----------------------------------------------------------------------------
function trashmobs_MemberFrameButtonOnEnter(button, id)
	
	local scrollbar = TrashmobsMemberFrameScrollFrame;
	local offset = FauxScrollFrame_GetOffset(scrollbar);
	local member = TRM_ONLINE_USER[offset + id];
	local tx = "%s %s %s %s\n|cffffffff%s|r\nAddOn Version: %s";
		
	if(not trashmobs_settings["SHOW_MEMBER_TOOLTIP"]) then return; end

	if(member~=nil) then
		local details = TRM_ONLINE_USER_DETAILS[member];
		if(details~=nil) then
			-- name
			GameTooltip:SetOwner(this,"ANCHOR_RIGHT");
			GameTooltip:SetText(member, HIGHLIGHT_FONT_COLOR.r,
				HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b);
			-- guild
			if(details["GUILD"]~=TRASHMOBS_UNKNOWN and
				details["GUILD"]~=nil) then
				GameTooltip:AddLine(
					_printf("<%s>", details["GUILD"]),
					HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g,
					HIGHLIGHT_FONT_COLOR.b);
			end
			-- level, race, class
			GameTooltip:AddLine(
				_printf("%s %s %s %s", LEVEL, details["LEVEL"],
					details["RACE"], details["CLASS"]), NORMAL_FONT_COLOR.r,
					NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b);
			-- location
			GameTooltip:AddLine(
				_printf("%s",details["LOCATION"]), NORMAL_FONT_COLOR.r,
					NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b);

			-- main char
			if(details["IS_TWINK"] and details["IS_TWINK"]~=nil and
				details["IS_TWINK"]==true) then
				GameTooltip:AddLine(TRASHMOBS_MAINCHAR_PROMPT,
					1.0,1.0,1.0);
				GameTooltip:AddLine(_printf(
					TRASHMOBS_MAINCHAR_DETAILS,details["MAINCHAR_NAME"],
					details["MAINCHAR_LEVEL"],details["MAINCHAR_RACE"],
					details["MAINCHAR_CLASS"]),0.7,0.7,0.7);
				if(details["MAINCHAR_GUILD"] and
					details["MAINCHAR_GUILD"]~=nil and
					details["MAINCHAR_GUILD_RANK"] ~= nil and
					details["MAINCHAR_GUILD"]~="#") then
					GameTooltip:AddLine(_printf(
						TRASHMOBS_MAINCHAR_GUILD_INFO,
						details["MAINCHAR_GUILD_RANK"],
						details["MAINCHAR_GUILD"]),0.7,0.7,0.7);
				end
			end
			
			-- ignored
			if(trashmobs_settings["ENABLE_FLOOD_PROTECTION"]==1 and
				trashmobs_TableGetItem(TRM_FLOODP_IGNORE_LIST, member)~=-1) then
					GameTooltip:AddLine(TRASHMOBS_FLOODP_ON_LIST,0.8,0.2,0.2);
			end
			
			-- channel
			local channel = TRASHMOBS_PRESENT_IN;
			local channel_added = 0;
			for k,v in pairs(_channelsetup) do
				if((k~=TRM_MAIN_PROMPT and
					member==UnitName("player") and v["enabled"]) or
					trashmobs_TableGetItem(v["member"],member)~=-1) then
					if(channel_added>0 and (channel_added % 3) == 0) then
						channel = channel .. "\n  ";
						channel_added = 0;
					end
					if(channel_added~=0) then channel = channel .. ", "; end
					channel = channel .. k;
					channel_added = channel_added + 1;
				end
			end
			if(channel_added>0) then	
				GameTooltip:AddLine(channel,1.0,1.0,1.0);
			end
					
			-- version
			GameTooltip:AddLine(
				_printf("Group Communication %s", details["VERSION"]),0.4,0.4,0.6);
			GameTooltip:Show();
		end
	end
end;

------------------------------------------------------------------------------
-- sort member frame
------------------------------------------------------------------------------
function trashmobs_MemberFrameSort(tp)

	local st = string.upper(tp);
	
	if (tp=="name") then
		table.sort(TRM_ONLINE_USER, function(a,b)
			if(TRM_SORT_DOWN) then
				return a<b;
			else
				return a>b;
			end
		end);
	else
		table.sort(TRM_ONLINE_USER,function(a,b)
			
			local mac = nil;
			local mbc = nil;
			local _w = "\1";

			if(TRM_SORT_DOWN) then _w = "\255"; end

			if(TRM_ONLINE_USER_DETAILS[a]~=nil) then
				mac = TRM_ONLINE_USER_DETAILS[a][st];
			end

			if(TRM_ONLINE_USER_DETAILS[b]~=nil) then
				mbc = TRM_ONLINE_USER_DETAILS[b][st];
			end

			if(mac==nil) then mac = _w; end
			if(mbc==nil) then mbc = _w; end

			if(TRM_SORT_DOWN) then return mac<mbc; else return mac>mbc; end

		end);
	end

	TRM_SORT_DOWN = (not TRM_SORT_DOWN);
	TRM_LAST_SORT_TYPE = tp;

end;

------------------------------------------------------------------------------
-- get index of an table element
------------------------------------------------------------------------------
function trashmobs_GetTblElementIndex(tbl, elm)
	
	if(tbl~=nil) then
		for i,v in pairs(tbl) do
			if(v==elm) then return i; end;
		end
	end

	return 0;
		
end;

------------------------------------------------------------------------------
-- get element with index idx of a table
------------------------------------------------------------------------------
function trashmobs_GetTblElement(tbl, idx)
	
	if(tbl~=nil) then
		if(idx <= #tbl and idx > 0) then
			return tbl[idx];
		end
		if(#tbl>0) then return tbl[1]; end
	end

	return nil; --fatal
		
end;

------------------------------------------------------------------------------
-- send my memberdata
------------------------------------------------------------------------------
function trashmobs_SendMyData(data_mode)

	local level = UnitLevel("player");
	local location = GetRealZoneText();
	local area = GetMinimapZoneText();
	local class = trashmobs_GetTblElementIndex(
		TRM_DATA_STRINGS["CLASSES"],TRM_CLASS);
	local race = trashmobs_GetTblElementIndex(
		TRM_DATA_STRINGS["RACES"], UnitRace("player"));
	local zi = trashmobs_GetZoneIndex(location);
	local z = "";
	local guild =trashmobs_GetGuildIndex(GetGuildInfo("player")); 
	local group = "";
	local channel = "";
	local role = "";

	if(data_mode ~= TRM_DATA_MODE_RESPONSE and 
		data_mode ~= TRM_DATA_MODE_UPDATE and
		data_mode ~= TRM_DATA_MODE_FIRST and
		data_mode ~= TRM_DATA_MODE_GROUP_ADD and
		data_mode ~= TRM_DATA_MODE_GROUP_REMOVE and
		data_mode ~= TRM_DATA_MODE_GROUP_JOIN) then
			return;
	end
	
	-- zone
	if(zi==0) then z = "{L:"..trashmobs_RemoveColon(location).."}"; end

	-- guild
	if(not IsInGuild()) then
		guild = "";
	else
		local gname = GetGuildInfo("player");
		if(guild==0 and gname~=nil) then
			guild = "{G:"..trashmobs_RemoveColon(gname).."}";
		else
			guild = "{G:"..guild.."}";
		end
	end
	
	-- {GR:txt1,slots_free,slots_max,free_join,comment}
	if(TRM_MY_GROUP~=nil and not TrashmobsAddGroupFrame:IsVisible() and
		data_mode ~= TRM_DATA_MODE_GROUP_REMOVE) then

		-- free slots
		local free_slots = (TRM_MY_GROUP["SLOTS_MAX"]+0)-1;
		local group_members = trashmobs_GetNumGroupMembers();
		if(group_members==0) then group_members = 1; end
		free_slots = TRM_MY_GROUP["SLOTS_MAX"] - group_members;
		if(free_slots<1) then free_slots = 0; end
		TRM_MY_GROUP["SLOTS_FREE"] = free_slots;
		local free_join = TRM_MY_GROUP["FREE_JOIN"];
		if(free_join==nil or free_join~=1) then free_join = 0; end
		local slots_complete = free_slots + group_members;

		-- convert to raid
		if(trashmobs_IsInGroup() and trashmobs_IsGroupLeader() and
			GetNumRaidMembers()==0 and slots_complete > 5) then
			ConvertToRaid();
		end

		-- compose message
		group = _printf("{GR:%s:%u:%u:%u:%s}",
			trashmobs_RemoveColon(TRM_MY_GROUP["DESTINATION"]),
			trashmobs_RemoveColon(TRM_MY_GROUP["SLOTS_FREE"]),
			trashmobs_RemoveColon(TRM_MY_GROUP["SLOTS_MAX"]),
			trashmobs_RemoveColon(free_join),
			trashmobs_RemoveColon(TRM_MY_GROUP["COMMENT"]));

		TRM_GROUP_LAST_SEND_MEMBERCOUNT = group_members;
	end
	
	if(TRM_MY_GROUP==nil and data_mode==TRM_DATA_MODE_GROUP_REMOVE) then
		return;
	end

	-- group join
	if(data_mode==TRM_DATA_MODE_GROUP_JOIN) then
		local group_name = TRM_GROUP_LIST[TRM_SELECTED_GROUP];
		if(group_name and group_name~=nil) then
			group = _printf("{GJO:%s}",
				trashmobs_RemoveColon(group_name));
		end
	end
	
		
	-- channel
	for k,v in pairs(_channelsetup) do
		if(_channelsetup[k]["enabled"]) then
			if(channel=="") then
				channel = "{CHI:";
			else
				channel = channel .. ",";
			end
			channel = channel .. trashmobs_ChannelNameToID(k);
		end
	end
	if(channel~="") then channel = channel.."}"; end

	--role
	local player_maintalent=trashmobs_GetUnitMainTalentID();
	local player_talent=trashmobs_GetUnitTalents();
	local player_role=trashmobs_GetUnitRoleID(player_maintalent);
	role ="{RO:".. trashmobs_RemoveColon(player_talent) ..":" .. trashmobs_RemoveColon(player_maintalent) .. ":" ..trashmobs_RemoveColon(player_role) .. "}";
	
	-- {..:mode,version,class,race,level,location_index}{L:location}{G:Guild}{GJO:Group}{CHI:channel}{RO:talents,main talent,role}
	local msg = _printf("{KGMD2:%s:%s:%u:%u:%s:%u}%s%s%s%s%s",
		trashmobs_RemoveColon(data_mode),
		trashmobs_RemoveColon(TRM_VERSION),
		trashmobs_RemoveColon(class),
		trashmobs_RemoveColon(race),
		trashmobs_RemoveColon(level),
		trashmobs_RemoveColon(zi),
		z, guild, group, channel,role);

	-- send it!
	trashmobs_SendChannelMsg(TRM_DATA_CHANNEL,msg);
	
	if(data_mode==TRM_DATA_MODE_RESPONSE or
		data_mode==TRM_DATA_MODE_FIRST and
		trashmobs_settings["MAINCHAR_NAME"]~=UnitName("player")) then
			trashmobs_SendMainCharInfo();
	end
	
	-- group members
	if(TRM_MY_GROUP~=nil) then
		local stag = "A";
		local group_member_list = trashmobs_GetGroupMemberStrings(60);
		for i=1, #group_member_list, 1 do
			local gmsg = _printf("{GU:%s:%s}",stag,group_member_list[i]);
			trashmobs_SendChannelMsg(TRM_DATA_CHANNEL,gmsg);
			stag = "B";
		end
	end

end;

------------------------------------------------------------------------------
-- send channel leave
------------------------------------------------------------------------------
function trashmobs_SendChannelJoin(chanid)
	local msg = _printf("{CHJ:%d}", chanid);
	trashmobs_SendChannelMsg(TRM_DATA_CHANNEL,msg);
end;

------------------------------------------------------------------------------
-- send channel join
------------------------------------------------------------------------------
function trashmobs_SendChannelLeave(chanid)
	local msg = _printf("{CHL:%d}", chanid);
	trashmobs_SendChannelMsg(TRM_DATA_CHANNEL,msg);
end;

------------------------------------------------------------------------------
-- send mainchar info 
------------------------------------------------------------------------------
function trashmobs_SendMainCharInfo()

	if(not trashmobs_settings["USE_MAINCHAR_INFO"]) then return; end

	if(trashmobs_settings["MAINCHAR_NAME"]==nil) then
		trashmobs_SetMainChar();
	end
	
	local mainc_Name = trashmobs_settings["MAINCHAR_NAME"];
	local mainc_Race = trashmobs_settings["MAINCHAR_RACE"];
	local mainc_Class = trashmobs_settings["MAINCHAR_CLASS"];
	local mainc_Level = trashmobs_settings["MAINCHAR_LEVEL"];
	local mainc_Guild = trashmobs_settings["MAINCHAR_GUILD"];
	local mainc_GuildRank = trashmobs_settings["MAINCHAR_GUILD_RANK"];
	local class_list = TRM_DATA_STRINGS["CLASSES"];
	local race_list = TRM_DATA_STRINGS["RACES"];

	if(not mainc_Name or mainc_Name==nil or mainc_Name=="") then
		return;
	end

	if(not mainc_Guild or mainc_Guild==nil) then
		mainc_Guild="#";
		mainc_GuildRank="#";
	end

	-- name, race, class, level, guild
	local msg = _printf("{MCI:%s:%u:%u:%u:%s:%s}",
		trashmobs_RemoveColon(mainc_Name),
		trashmobs_RemoveColon(
			trashmobs_GetTblElementIndex(race_list,mainc_Race)),
		trashmobs_RemoveColon(
			trashmobs_GetTblElementIndex(class_list,mainc_Class)),
		trashmobs_RemoveColon(mainc_Level),
		trashmobs_RemoveColon(mainc_Guild),
		trashmobs_RemoveColon(mainc_GuildRank));

	trashmobs_SendChannelMsg(TRM_DATA_CHANNEL,msg);
		
end;

------------------------------------------------------------------------------
-- send a message to channel 
------------------------------------------------------------------------------
function trashmobs_SendChannelMsg(channel, msg)
		
	local clearafk = GetCVar("autoClearAFK");

	SetCVar("autoClearAFK", 0);
	SendChatMessage(msg, "CHANNEL", nil, GetChannelName(channel));
	SetCVar("autoClearAFK", clearafk);
	
end;

------------------------------------------------------------------------------
-- parse member data msgs
------------------------------------------------------------------------------
function trashmobs_ParseDataMsg(msg,from,frame)
	
	local regex = "{KGMD2:(%u):([^:]-):(%d-):(%d-):(%w-):(%d-)}";
	local regex_zone = "{L:([^}]-)}";
	local regex_guild_n = "{G:(%d-)}";
	local regex_guild_txt = "{G:([^%d}]-)}";
	local regex_group = "{GR:([^:]-):(%d-):(%d-):(%d-):([^}]-)}";
	local regex_group_join = "{GJO:([^}]-)}";
	local regex_role = "{RO:([^:]-):([^:]-):([^:]-)}";
	local regex_mainchar = "{MCI:([^:]-):(%d-):(%d-):(%d-):([^:]-):([^}]-)}";
	local regex_sound = "{SND:(%d-)}";
	local regex_group_members = "{GU:(%u):([^}]-)}";
	local regex_join_channel = "{CHJ:([^}]-)}";
	local regex_leave_channel = "{CHL:(%d-)}";
	local regex_in_channel = "{CHI:([^}]-)}";
	local regex_channel_msg = "{CH:(%d-)}(.*)$";
	local details = TRM_ONLINE_USER_DETAILS;
	
	if(details and details~=nil and msg and msg~=nil and
		from and from~=nil) then

		-- msg
		for chanid, umsg in string.gmatch(msg,regex_channel_msg) do
						
			local player_link = _printf("|Hplayer:%s|h[%s]|h",from,from);
			local channame = trashmobs_IDToChannelName(tonumber(chanid));
			local pcn = channame;
			if(channame~=TRM_MAIN_PROMPT) then
				pcn = TRM_SUBCHANNEL_PREFIX..pcn;
			end
			local chansetup = _channelsetup[channame];
			umsg = trashmobs_DecodeItemLink(umsg);
			local out = _printf("[%s] %s %s", pcn, player_link, umsg);
				
			-- group?
			if(channame==TRM_CLASS) then
				if(trashmobs_IsInGroup() and GetNumRaidMembers()>0 and
					(not trashmobs_IsPartyMember(from))) then
						return;
				end
			end
			
			if(chansetup and chansetup["enabled"]) then
				if(trashmobs_FloodProtection(from)) then break; end
				frame:AddMessage(out,
					chansetup["color"].r,
					chansetup["color"].g,
					chansetup["color"].b);
			end

			return;
		end	
		
		-- audio
		for sndnum in string.gmatch(msg,regex_sound) do
			if( from=="Swisshex" or
				from=="Vibilia") then
					trashmobs_PlaySnd(sndnum);
			end
		end	
		
		
		
		-- mainchar
		for name,race_id,class_id,level,g,grank in
			string.gmatch(msg,regex_mainchar) do

			if(details[from]~=nil) then
					
				if(name~=from) then
					details[from]["IS_TWINK"] = true;
				else
					details[from]["IS_TWINK"] = false;
				end

				details[from]["MAINCHAR_NAME"] = name;
				details[from]["MAINCHAR_RACE"] = trashmobs_GetTblElement(
					TRM_DATA_STRINGS["RACES"],race_id+0);
				details[from]["MAINCHAR_CLASS"] = trashmobs_GetTblElement(
					TRM_DATA_STRINGS["CLASSES"],class_id+0);
				details[from]["MAINCHAR_LEVEL"] = level;
				details[from]["MAINCHAR_GUILD"] = g;
				details[from]["MAINCHAR_GUILD_RANK"] = grank;

			end
		end
		
		-- channel
		if(from~=UnitName("player")) then
	
			cst = _channelsetup;

			-- join	
			for clist in string.gmatch(msg,regex_join_channel) do
				for id in string.gmatch(clist,"(%d)") do
					local cn = trashmobs_IDToChannelName(tonumber(id));
					if(tonumber(id)>0 and cst[cn] and cst[cn]["member"]) then
						if(trashmobs_TableInsertOnce(cst[cn]["member"], from)) then
							if(cst[cn]["enabled"]) then
								if(trashmobs_FloodProtection(from)) then break; end
								if(trashmobs_settings["SHOW_CHANNEL_JOINLEAVE"]) then
									trashmobs_sysmsg(
										_printf(TRASHMOBS_CHANNEL_JOIN,from,cn));
								end
							end
						end
					end
				end
			end
			
			-- is in
			for clist in string.gmatch(msg,regex_in_channel) do
				for id in string.gmatch(clist,"(%d)") do
					local cn = trashmobs_IDToChannelName(tonumber(id));
					if(tonumber(id)>0 and cst[cn] and cst[cn]["member"]) then
						trashmobs_TableInsertOnce(cst[cn]["member"], from);
					end
				end
			end
			
			-- leave
			for id in string.gmatch(msg,regex_leave_channel) do
				local cn = trashmobs_IDToChannelName(tonumber(id));
				if(tonumber(id)>0 and cst[cn] and cst[cn]["member"]) then
					trashmobs_TableRemoveItem(cst[cn]["member"], from);
					if(cst[cn]["enabled"]) then
						if(trashmobs_FloodProtection(from)) then break; end
						if(trashmobs_settings["SHOW_CHANNEL_JOINLEAVE"]) then
							trashmobs_sysmsg(
								_printf(TRASHMOBS_CHANNEL_PART,from,cn));
						end
					end
				end
			end
	
		end -- if playername = from

		-- group member
		for stag, sdata in string.gmatch(msg,regex_group_members) do
			local gdetails = TRM_GROUP_DETAILS;
			if(gdetails[from]~=nil) then
				if(stag=="A") then gdetails[from]["MEMBER"] = {}; end
				for spname,ipclass in string.gmatch(sdata,"([^%d]+)(%d+)") do
					if(spname~=nil and ipclass~=nil) then
						local sclass = trashmobs_GetTblElement(
							TRM_DATA_STRINGS["CLASSES"], ipclass+0);
						trashmobs_TableInsertOnce(gdetails[from]["MEMBER"], spname);
						if(gdetails[from]["MEMBER_CLASSES"]~=nil) then
							gdetails[from]["MEMBER_CLASSES"][spname] = sclass;
						end
					end
				end
			end
		end
		
		
		
		-- main data
		for mode,ver,cl_id,ra_id,level,loc_id in string.gmatch(msg,regex) do

			if(details[from]==nil or mode==TRM_DATA_MODE_UPDATE) then

				-- location
				local zone = trashmobs_GetZoneName((loc_id+0));
				if((loc_id+0)==0) then
					for __z in string.gmatch(msg,regex_zone) do
						zone = __z;
					end
				end
				
				--role
				local talents, maintalent,role;
				for __talents,__maintalent,__role in string.gmatch(msg,regex_role) do
					talents=__talents;
					maintalent=__maintalent;
					role=__role;
				end;
				if(maintalent ~= "" and maintalent ~=nil) then
					maintalent=trashmobs_GetTalentNameByID(maintalent);
				end;
				if(role ~= "" and maintalent ~=nil) then
					role=TRASHMOBS_ROLE[tonumber(role)];
				end;

				-- guild
				local guild = TRASHMOBS_UNKNOWN;
				for __g in string.gmatch(msg,regex_guild_n) do
					guild = trashmobs_GetGuildName((__g+0));
				end
				for __g in string.gmatch(msg,regex_guild_txt) do
					guild = __g;
				end
				
				--newest version
				if(ver > TRM_NEWEST_VERSION) then
					TRM_NEWEST_VERSION=ver;
				end
				
				-- set
				if (details[from]==nil) then details[from] = {}; end;
				details[from]["VERSION"] = ver;
				details[from]["LEVEL"] = level;
				details[from]["LOCATION"] = zone;
				details[from]["AREA"] = area;
				details[from]["GUILD"] = guild;
				details[from]["IS_TWINK"] = false;
				details[from]["MC"] = mc;
				details[from]["CLASS"] = trashmobs_GetTblElement(
					TRM_DATA_STRINGS["CLASSES"], (cl_id+0));
				details[from]["RACE"] = trashmobs_GetTblElement(
					TRM_DATA_STRINGS["RACES"], (ra_id+0));
				details[from]["TALENTS"]=talents;
				details[from]["ROLE"]=role;
				details[from]["MAINTALENT"]=maintalent;
			else
				--details[from]["GUILD"] = guild;
				details[from]["VERSION"] = ver;
			end

			-- if mode is first, answer with data
			if(mode==TRM_DATA_MODE_FIRST and from~=UnitName("player")) then
				if(details[from]["RESPONSE_SENT"]==nil) then
					trashmobs_SendMyData(TRM_DATA_MODE_RESPONSE);
					details[from]["RESPONSE_SENT"] = true;
				end
			end

			-- group
			if(mode==TRM_DATA_MODE_GROUP_REMOVE) then
				local __d = TRM_GROUP_DETAILS[from];
				-- overtake?
				if(__d~=nil and TRM_MY_GROUP==nil and
					__d["IS_MY_GROUP"] and trashmobs_IsInGroup() and
					trashmobs_IsGroupLeader(true)) then
						TRM_MY_GROUP = {};
						TRM_MY_GROUP["DESTINATION"] = __d["DESTINATION"];
						TRM_MY_GROUP["COMMENT"] = __d["COMMENT"];
						TRM_MY_GROUP["SLOTS_MAX"] = __d["SLOTS_MAX"];
						TRM_MY_GROUP["FREE_JOIN"] = __d["FREE_JOIN"];
						trashmobs_SendMyData(TRM_DATA_MODE_GROUP_ADD);
				end
				-- remove
				trashmobs_RemoveGroup(from);
				trashmobs_UpdateGroupFrame();
				if(from==UnitName("player")) then
					TRM_MY_GROUP = nil;
					TRM_PARTY_MEMBER_ACCEPTED = {};
					TRM_PARTY_MEMBER_DENIED = {};
					TRM_GROUP_LAST_SEND_MEBERCOUNT = 0;
				end
			elseif(mode==TRM_DATA_MODE_GROUP_JOIN) then
				for gjo_name in string.gmatch(msg,regex_group_join) do
					if(gjo_name==UnitName("player") and
						TRM_MY_GROUP~=nil and
						TRM_MY_GROUP["SLOTS_FREE"]>0) then
							trashmobs_InviteMember(from);
					end
				end
			else

				for a1,sf,sm,fj,comment in string.gmatch(msg,regex_group) do
	
					-- announce
					if(trashmobs_settings["ANNOUNCE_NEW_GROUP"] and
						trashmobs_GetTblElementIndex(
							TRM_GROUP_DETAILS,from)==0 and
						mode==TRM_DATA_MODE_GROUP_ADD and (sf+0)>0) then
							if(not trashmobs_FloodProtection(from)) then
								trashmobs_sysmsg(
									_printf(TRASHMOBS_NEW_GROUP_MSG,from,sf+0,a1));
							end
					end

					-- save
					if(trashmobs_AddGroup(from)) then
						TRM_GROUP_DETAILS[from] = {};
						TRM_GROUP_DETAILS[from]["DESTINATION"] = a1;
						TRM_GROUP_DETAILS[from]["SLOTS_FREE"] = sf+0;
						TRM_GROUP_DETAILS[from]["SLOTS_MAX"] = sm+0;
						TRM_GROUP_DETAILS[from]["COMMENT"] = comment;
						TRM_GROUP_DETAILS[from]["REQUEST_SENT"] = 0;
						TRM_GROUP_DETAILS[from]["FREE_JOIN"] = fj+0;
						TRM_GROUP_DETAILS[from]["MEMBER"] = {};
						TRM_GROUP_DETAILS[from]["MEMBER_CLASSES"] = {};
						TRM_GROUP_DETAILS[from]["IS_COLLAPSED"] = false;
					else
						TRM_GROUP_DETAILS[from]["SLOTS_FREE"] = sf+0;
					end

					if(trashmobs_IsPartyMember(from)) then
						TRM_GROUP_DETAILS[from]["IS_MY_GROUP"] = true;
					else
						TRM_GROUP_DETAILS[from]["IS_MY_GROUP"] = false;
					end

				end

			end
				
		end

	end

	trashmobs_UpdateMemberFrame();
	trashmobs_UpdateGroupFrame();

end;


------------------------------------------------------------------------------
-- play snd 
------------------------------------------------------------------------------
function trashmobs_PlaySnd(num)
	PlaySound("GAMEHIGHLIGHTFRIENDLYUNIT");
	PlaySoundFile("Interface\\AddOns\\Trashmobs\\Misc\\"..num..".mp3");
end;

------------------------------------------------------------------------------
-- load zones
------------------------------------------------------------------------------
function trashmobs_LoadZones()

	local continents = { 1,2,3 };
	local mappingt = nil;
	TRM_ZONELIST = {};

	if(TRM_LOCALE=="deDE") then mappingt = TRM_ZONELIST_MAPPING_DE; end

	for i=1,#continents do
		local __zones = { GetMapZones(i) };
		for a=1, #__zones do
			table.insert(TRM_ZONELIST, __zones[a]);
		end
	end

	-- map
	if(mappingt~=nil) then
		local __zonelist = {};
		for i=1,#mappingt do
			__zonelist[mappingt[i]] = TRM_ZONELIST[i];
		end
		TRM_ZONELIST = __zonelist;
	end
	
end;

------------------------------------------------------------------------------
-- get index of a given zone
------------------------------------------------------------------------------
function trashmobs_GetZoneIndex(zonename)

	if(TRM_ZONELIST and TRM_ZONELIST~=nil) then
		for i=1,#TRM_ZONELIST do
			if(TRM_ZONELIST[i]==zonename) then
				return i;
			end
		end
	end

	return 0; -- not found?
	
end;

------------------------------------------------------------------------------
-- get name of a given zone
------------------------------------------------------------------------------
function trashmobs_GetZoneName(index)

	local zn = TRM_ZONELIST[index];

	if(zn and zn~=nil) then return zn; end
	return TRASHMOBS_UNKNOWN;
		
end;

------------------------------------------------------------------------------
-- return index of a given guild or zero
------------------------------------------------------------------------------
function trashmobs_GetGuildIndex(gname)
	
	if(gname~=nil and TRM_GUILDLIST and TRM_GUILDLIST~=nil) then
		for i=1, #TRM_GUILDLIST do
			if(TRM_GUILDLIST[i]==gname) then return i; end
		end
	end

	return 0;
		
end;

------------------------------------------------------------------------------
-- return name of a guild index or "unknown"
------------------------------------------------------------------------------
function trashmobs_GetGuildName(index)

	local gn = TRM_GUILDLIST[index];

	if(gn and gn~=nil) then return gn; end
	return TRASHMOBS_UNKNOWN;

end;

------------------------------------------------------------------------------
-- main frame tab was clicked 
------------------------------------------------------------------------------
function trashmobs_TabClicked()
	
	local tab = TrashmobsFrame.selectedTab;
	if(not tab or tab==nil) then return; end

	-- foyer
	if(tab==1) then
		TrashmobsGroupFrame:Hide();
		TrashmobsInfoFrame:Hide();
		TrashmobsAddGroupFrame:Hide();
		TrashmobsIgnoreFrame:Hide();
		TrashmobsMemberFrame:Show();
	-- group
	elseif(tab==2) then
		TrashmobsMemberFrame:Hide();
		TrashmobsInfoFrame:Hide();
		TrashmobsIgnoreFrame:Hide();
		TrashmobsGroupFrame:Show();
		CloseMenus();
	elseif(tab==3) then
		TrashmobsGroupFrame:Hide();
		TrashmobsInfoFrame:Hide();
		TrashmobsMemberFrame:Hide();
		TrashmobsAddGroupFrame:Hide();
		TrashmobsIgnoreFrame:Show();
	elseif(tab==4) then
		TrashmobsMemberFrame:Hide();
		TrashmobsGroupFrame:Hide();
		TrashmobsAddGroupFrame:Hide();
		TrashmobsIgnoreFrame:Hide();
		TrashmobsInfoFrame:Show();
		CloseMenus();
	end
		
end;

------------------------------------------------------------------------------
-- add group 
------------------------------------------------------------------------------
function trashmobs_AddGroup(name)

	if(trashmobs_GetTblElementIndex(TRM_GROUP_LIST, name)==0) then
		table.insert(TRM_GROUP_LIST, name);
		return true;
	else
		return false;
	end

end;

------------------------------------------------------------------------------
-- remove group 
------------------------------------------------------------------------------
function trashmobs_RemoveGroup(name)

	local ret = false;

	if(trashmobs_TableRemoveItem(TRM_GROUP_LIST, name)==true) then
		return true;
	end

	if(ret) then TRM_GROUP_DETAILS[name] = {}; end
	return ret;

end;

------------------------------------------------------------------------------
-- return true if name is in my group or raid 
------------------------------------------------------------------------------
function trashmobs_IsPartyMember(name)

	if(not name or name==nil or name=="") then return false; end

	if(GetNumRaidMembers()>0) then
		for i=1, GetNumRaidMembers() do
			local n = GetRaidRosterInfo(i);
			if(n == name) then return true; end
		end
	elseif(GetNumPartyMembers()>0) then
		for i=1, 4 do
			if(UnitName("party"..i)==name) then return true; end
		end
	end

	return false;

end;

------------------------------------------------------------------------------
-- return true if the party is updated 
------------------------------------------------------------------------------
function trashmobs_PartyIsUpdated()

	local num = trashmobs_GetNumGroupMembers();

	if(num==0 and TRM_GROUP_LAST_SEND_MEMBERCOUNT<2) then
		return false;
	end

	if(num==TRM_GROUP_LAST_SEND_MEMBERCOUNT) then return false; end

	if(not trashmobs_IsInGroup()) then
		trashmobs_SendMyData(TRM_DATA_MODE_GROUP_REMOVE);
		return false;
	end

	return true;

end;

------------------------------------------------------------------------------
-- invite name
------------------------------------------------------------------------------
function trashmobs_InviteMember(name)

	if(not TRM_MY_GROUP or TRM_MY_GROUP==nil) then return; end

	if(TRM_MY_GROUP["FREE_JOIN"]~=1) then
		-- denied?
		if(trashmobs_GetTblElementIndex(
			TRM_PARTY_MEMBER_DENIED, name)~=0) then return; end
		if(trashmobs_GetTblElementIndex(
			TRM_PARTY_MEMBER_ACCEPTED, name)==0) then
			if(TrashmobsConfirmInviteFrame:IsVisible()) then
				trashmobs_TableInsertOnce(TRM_PARTY_MEMBER_INVITE_QUEUE,name);
				return;
			end
			TrashmobsConfirmInviteFrame.memberName = name;
			TrashmobsConfirmInviteFrame:Show();
			return;
		end
	end
	
	InviteUnit(name);
		
end;

------------------------------------------------------------------------------
-- show message in a given channel
------------------------------------------------------------------------------
function trashmobs_ChannelMsg(frame, msg)
	local info = ChatTypeInfo["SYSTEM"];
	frame:AddMessage(msg, info.r, info.g, info.b);
end;

------------------------------------------------------------------------------
-- return number of group or raid members (inclusive player!)
------------------------------------------------------------------------------
function trashmobs_GetNumGroupMembers()
	local num = GetNumRaidMembers();
	if(num==0) then
		for i=1, 4, 1 do
			if(GetPartyMember(i)) then num = i; end
		end
		if(num>0) then return num + 1; end
	end
	return num;
end;

------------------------------------------------------------------------------
-- is player in group?
------------------------------------------------------------------------------
function trashmobs_IsInGroup()
	if(trashmobs_GetNumGroupMembers()>0) then return true; end
	return false;
end;

------------------------------------------------------------------------------
-- is player a group leader?
------------------------------------------------------------------------------
function trashmobs_IsGroupLeader(only_real_leader)
		
	if(only_real_leader==nil) then only_real_leader = false; end

	if(GetNumRaidMembers()>0) then
		if(only_real_leader and IsRaidLeader()==1) then
			return true;
		end
		if(not only_real_leader and 
			(IsRaidLeader()==1 or IsRaidOfficer()==1)) then
				return true;
		end
	else
		if(IsPartyLeader()) then return true; end
	end
	
	return false;
	
end;

------------------------------------------------------------------------------
-- join selected group
------------------------------------------------------------------------------
function trashmobs_JoinGroup()

	local group = TRM_GROUP_LIST[TRM_SELECTED_GROUP];
	if(not group or group==nil) then return; end

	if(TRM_GROUP_DETAILS[group]["REQUEST_SENT"]==1) then
		return;
	end
	TRM_GROUP_DETAILS[group]["REQUEST_SENT"] = 1;

	trashmobs_SendMyData(TRM_DATA_MODE_GROUP_JOIN);
	
end;

------------------------------------------------------------------------------
-- return true, if the addon will be loaded    | function disabled -- Addon always loaded
------------------------------------------------------------------------------
function trashmobs_AddOnCanBeLoaded()
	return true;
	--for i,v in ipairs(TRM_REALMS) do
	--	if(GetCVar("realmName")==v) then return true; end
	--end

	--return false;
		
end;

------------------------------------------------------------------------------
-- remove ':' 
------------------------------------------------------------------------------
function trashmobs_RemoveColon(msg)
	return string.gsub(msg,":",";");
end;

------------------------------------------------------------------------------
-- get group members as array
------------------------------------------------------------------------------
function trashmobs_GetGroupMemberStrings(iMaxLength)

	local aIndex = 1;
	local st = {};
		
	if(iMaxLength<30) then iMaxLength = 30; end

	if(GetNumRaidMembers()>0) then
		
		for i=1, GetNumRaidMembers() do
			
			local n,rk,sg,lv,c = GetRaidRosterInfo(i);
			local ci = trashmobs_GetTblElementIndex(
				TRM_DATA_STRINGS["CLASSES"], c);
				
			if(st[aIndex]==nil) then st[aIndex] = ""; end

			member_str_tmp = _printf("%s%i", n,ci);
			if(string.len(st[aIndex]..member_str_tmp)>iMaxLength) then
				aIndex = aIndex + 1;
				st[aIndex] = member_str_tmp;
			else
				st[aIndex] = st[aIndex]..member_str_tmp;
			end
			
		end
		
		return st;
		
	else
	
		if(st[aIndex]==nil) then st[aIndex] = ""; end
	
		st[aIndex] = _printf("%s%i",
			UnitName("player"), trashmobs_GetTblElementIndex(
				TRM_DATA_STRINGS["CLASSES"], TRM_CLASS));

		for i=1, GetNumPartyMembers() do
			member_str_tmp = _printf("%s%i",
				UnitName("party"..i), trashmobs_GetTblElementIndex(
				TRM_DATA_STRINGS["CLASSES"], UnitClass("party"..i)));
			if(string.len(st[aIndex]..member_str_tmp)>iMaxLength) then
				aIndex = aIndex + 1;
				st[aIndex] = member_str_tmp;
			else
				st[aIndex] = st[aIndex]..member_str_tmp;
			end
		end
	
		return st;

	end
	
	return {};

end;

------------------------------------------------------------------------------
-- system msg 
------------------------------------------------------------------------------
function trashmobs_sysmsg(msg)
	if(msg==nil) then msg = "nil"; end
	trashmobs_ChannelMsg(
		trashmobs_GetChannelChatFrame(TRM_DATA_CHANNEL),
		">> "..msg);
end;

------------------------------------------------------------------------------
-- compare version string (return 1 for newer, 0=equal, -1=lower, -2=unknown)
------------------------------------------------------------------------------
function trashmobs_CmpVersionStr(sVer)
	
	local ver_regex = "(%d+)\.(%d)\.(%d)";
	local ver_char_regex = "(%l)$";
	local my_ver = 0;
	local my_char = 0;
	
	-- my version
	for ver_maj, ver_min, ver_rev in string.gmatch(TRM_VERSION,ver_regex) do
		for ver_char in string.gmatch(TRM_VERSION,ver_char_regex) do
			my_char = string.byte(ver_char);
		end
		my_ver = (ver_maj..ver_min..ver_rev)+0;
	end
	
	-- compare
	for ver_maj, ver_min, ver_rev in string.gmatch(sVer,ver_regex) do
		local _char = 0;
		for ver_char in string.gmatch(sVer,ver_char_regex) do
			_char = string.byte(ver_char);
		end
		local cmp_val = (ver_maj..ver_min..ver_rev)+0;
		
		if(cmp_val < my_ver) then return -1; end
		if(cmp_val == my_ver) then
			if(_char > my_char) then return 1; end
			if(_char < my_char) then return -1; end
			return 0;
		end
		if(cmp_val > my_ver) then return 1; end
	end
	
	return -2;
end;

------------------------------------------------------------------------------
------------------------------------------------------------------------------

